Skip to content

feat(dify-plugin): port external PRs#28

Merged
mislavivanda merged 21 commits into
mainfrom
port/dify-external-prs
Jul 14, 2026
Merged

feat(dify-plugin): port external PRs#28
mislavivanda merged 21 commits into
mainfrom
port/dify-external-prs

Conversation

@mislavivanda

@mislavivanda mislavivanda commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

This work is based on the PRs from the previous standalone Dify plugin repo which were created by @hjpinheiro.


Summary by cubic

Ports the remaining external Dify plugin work and adds file, git, lifecycle, and background-service tools for Daytona sandboxes. Improves reliability and performance with a cached client, safer cloning/archiving and file transfers, broader port validation, and richer outputs.

  • New Features

    • File ops: list, read, write, glob search, and content search inside a sandbox (list_files, read_file, write_file, search_files, find_in_files).
    • Git and lifecycle: clone repos, list sandboxes, and start/stop/archive (git_clone, list_sandboxes, manage_sandbox).
    • Background services: start long-running processes and fetch logs (start_service, get_service_logs).
    • Tool upgrades: run_command uses process.exec with optional cwd, env_vars, and timeout; run_code emits chart PNGs as image blobs and adds chart metadata.
  • Performance & Reliability

    • Cache the Daytona client per credentials with a collision-proof key; map generic SDK errors to clear messages when fetching sandboxes.
    • Enforce a 100 MB limit across upload, download, read, and write.
    • Hardened behaviors: redact credentials in git_clone without breaking IPv6/port URLs; drop branch when commit_id is set; validate and cap list_sandboxes via limit; stop before archiving and treat already-archived sandboxes as a no-op; unique session IDs and cleanup if launch fails in start_service; reject negative run_command timeouts; clearer get_service_logs output joining.
    • Accept ports 1–65535 in get_preview_url; treat blank numeric fields in create_sandbox as unset.
    • Align tool schemas/docs with current SDK behavior and register them in provider/daytona.yaml.

Written for commit 61d38ce. Summary will update on new commits.

Review in cubic

mislavivanda and others added 9 commits July 13, 2026 16:20
…rm fields

Ports the still-relevant parts of PR #1 from the archived daytona/dify-plugin. The SDK-compat list() call and SDK version pin were already handled by the monorepo cubic-review fixes, so this ports the remainder: widen get_preview_url to ports 1-65535 (was 3000-9999), and treat blank auto_stop_interval/cpu/memory/disk form values as unset so Dify's empty-string inputs never reach to_int(). Original work by @hjpinheiro (daytona/dify-plugin#1).

Co-authored-by: hpiclaranet <hpiclaranet@users.noreply.github.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
Ports PR #2 from the archived daytona/dify-plugin: a shared MAX_FILE_SIZE (100 MB) in _client.py, checked against file.blob before upload and against get_file_info().size (pre-download) plus content length (post-download) — preventing a large file from OOM-crashing the plugin daemon. Original work by @hjpinheiro (daytona/dify-plugin#2).

Co-authored-by: hpiclaranet <hpiclaranet@users.noreply.github.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
… error handling

Ports PR #3 from the archived daytona/dify-plugin: build_client() now caches one Daytona instance per credential hash (the plugin daemon is long-lived, so this avoids re-creating an HTTP session on every tool call), and get_sandbox() also maps a generic DaytonaError to a clear ValueError. Omitted the PR's unused module logger to keep the file lint-clean. Original work by @hjpinheiro (daytona/dify-plugin#3).

Co-authored-by: hpiclaranet <hpiclaranet@users.noreply.github.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
Ports PR #4 from the archived daytona/dify-plugin: replaces the session-based create_session/execute_session_command/delete_session dance with a single sandbox.process.exec() call, adding optional working directory, environment variables (JSON), and timeout. Uses the monorepo's to_int() for the timeout param and guards a missing command. Original work by @hjpinheiro (daytona/dify-plugin#4).

Co-authored-by: hpiclaranet <hpiclaranet@users.noreply.github.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
Ports PR #5 from the archived daytona/dify-plugin: when Python code produces charts (matplotlib/etc.), the Daytona SDK returns them as base64 PNGs in response.artifacts.charts. These were previously discarded; now each is decoded and yielded as an image/png blob message so users see rendered charts in Dify, with chart metadata added to the JSON result. Original work by @hjpinheiro (daytona/dify-plugin#5).

Co-authored-by: hpiclaranet <hpiclaranet@users.noreply.github.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
Ports PR #6 from the archived daytona/dify-plugin: adds list_files, read_file, write_file, search_files and find_in_files tools (with yaml manifests) for working with files inside a sandbox, and registers them in provider/daytona.yaml. Original work by @hjpinheiro (daytona/dify-plugin#6).

Co-authored-by: hpiclaranet <hpiclaranet@users.noreply.github.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
Ports PR #7 from the archived daytona/dify-plugin: adds git_clone (clone a repo into a sandbox), list_sandboxes (list with state filter), and manage_sandbox (start/stop/archive), plus yaml manifests, registered in provider/daytona.yaml. Original work by @hjpinheiro (daytona/dify-plugin#7).

Co-authored-by: hpiclaranet <hpiclaranet@users.noreply.github.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
…ervice_logs)

Ports PR #8 from the archived daytona/dify-plugin: start_service runs a long-lived command in an async session, and get_service_logs retrieves its output — enabling background processes (web servers, watchers) in a sandbox. Registered in provider/daytona.yaml. Original work by @hjpinheiro (daytona/dify-plugin#8).

Co-authored-by: hpiclaranet <hpiclaranet@users.noreply.github.com>
Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
Verifying the ported PRs against the Daytona SDK reference surfaced drift vs the pinned 0.193+ (the fork targeted ~0.187):

- start_service: SessionExecuteRequest field is run_async, not var_async.
- find_in_files: fs.find_files returns a flat list[Match] (.file/.line/.content), not nested r.matches/r.file.path.
- search_files: fs.search_files returns SearchFilesResponse (.files), not a list (len()/JSON on it would fail).
- list_sandboxes: remove unused SandboxState import (lint error).
- run_code: chart PNGs are on chart.png (chart.elements are data points, not images) — the old path delivered zero charts; also serialize the ChartType enum.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 30 files

You’re at about 93% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread apps/dify-plugin/_client.py Outdated
Comment thread apps/dify-plugin/tools/run_command.py
Comment thread apps/dify-plugin/tools/download_file.py
Comment thread apps/dify-plugin/tools/git_clone.py Outdated
Comment thread apps/dify-plugin/tools/manage_sandbox.py Outdated
Comment thread apps/dify-plugin/tools/run_code.py
Comment thread apps/dify-plugin/tools/manage_sandbox.py
Comment thread apps/dify-plugin/tools/get_preview_url.yaml
Comment thread apps/dify-plugin/tools/list_files.yaml
Comment thread apps/dify-plugin/tools/run_command.yaml
cubic review: read_file bypassed the size guard (loaded arbitrary files into memory) and write_file could upload past the shared limit; both now check size before/after transfer. upload_file now prechecks file.size metadata before materializing the blob. write_file also drops the empty-file second get_file_info lookup in favor of len(encoded).

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
…ommit_id is set

cubic review: embedded HTTPS userinfo in the repo URL was echoed back in both JSON and text output — now redacted (raw URL still used for the clone). Also stop sending/reporting branch when commit_id is provided (commit produces a detached checkout and overrides branch).

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
…te limit

cubic review: ListSandboxesQuery.limit is the page size, and list(daytona.list(...)) paged through the whole account regardless of limit. Use itertools.islice to collect at most 'limit' items, and validate limit via to_int as a positive whole number (rejecting fractional/non-positive values).

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
…ndbox

cubic review: archiving a running sandbox failed because Daytona requires it to be stopped first — the archive action now stops (which blocks until fully stopped) unless already stopped/archived. Switch both lookups to _client.get_sandbox for consistent not-found errors, and drop the redundant wait_for_sandbox_start() since Sandbox.start() already blocks until ready.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
…ch failure

cubic review: two services started in the same sandbox within one second shared session id svc-<int(time.time())>, so the second create_session collided. Use a UUID-based id. Also delete the freshly created session if execute_session_command fails so failed launches don't accumulate stale sessions.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
cubic review: negative timeouts were forwarded to the SDK where they become an invalid command/HTTP timeout. Validate timeout >= 0 (0/empty still means no timeout) so callers get a deterministic error.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
cubic review: report a neutral '(no output)' instead of '(no output yet)' for completed silent commands, and only insert a newline between stdout and stderr when both are non-empty and stdout doesn't already end with one (avoids a leading newline for stderr-only output and doubled newlines).

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
cubic review: the cache key concatenated api_key/api_url with a single ':' separator, so distinct pairs could hash to the same entry and reuse another credential set's client. Length-prefix each field before hashing.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
cubic review, documentation-only fixes:

- run_command: describe combined 'output' contract (stdout+stderr) in the README feature list and Tool Reference, and document cwd/env_vars/timeout inputs; timeout help now explains empty = Daytona default and 0 = no timeout

- git_clone: set the private-repo password/token to form: form so it is a preset encrypted credential rather than an LLM-inferred argument

- start_service: tell the model to bind previewable services to 0.0.0.0 so get_preview_url is reachable

- find_in_files: describe the pattern as a text pattern (SDK/Toolbox does not document regex support)

- get_preview_url: README port range corrected to 1-65535 to match the schema

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
cubic review: run_code now emits generated charts as PNG blobs and adds charts_count/charts JSON fields, but neither the tool description nor the README output contract mentioned them. Document both so users and the LLM can discover the behavior.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 16 files (changes from recent commits).

You’re at about 95% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/dify-plugin/tools/manage_sandbox.py Outdated
Comment thread apps/dify-plugin/tools/git_clone.py Outdated
cubic review follow-up: _redact_url used parts.hostname (which strips IPv6 brackets, turning [::1]:8080 into the malformed ::1:8080) and parts.port (which can raise on a malformed port, outside the try). Keep the netloc after the last '@' instead, which drops only the userinfo while preserving valid host:port / IPv6 syntax.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
cubic review follow-up: archive() requires a stopped sandbox, so calling it on an already-archived sandbox can fail. Treat 'archived' as idempotent success and only stop/archive other states.

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
@mislavivanda mislavivanda merged commit e9ef4fb into main Jul 14, 2026
10 checks passed
@mislavivanda mislavivanda deleted the port/dify-external-prs branch July 14, 2026 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant